package com.ctsi.commons.event;

import com.ctsi.commons.event.config.Event;
import com.ctsi.commons.util.ObjectType;
import com.ctsi.commons.util.UtilValidate;
import com.ctsi.commons.util.factory.ObjectFact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 事件发布实现
 * 
 * @author zhangjw
 * 
 */
public class Observable implements EventPublisher {
	private static final Logger log = LoggerFactory.getLogger(Observable.class);

	/**
	 * key: 事件id ,value 监听器
	 */
	private final ConcurrentMap<String, List<Observer>> observerMap;

	/**
	 * key:事件eventid value:事件对象列表
	 */
	private final ConcurrentMap<String, EventList> eventMap;

	public Observable() {
		observerMap = new ConcurrentHashMap<String, List<Observer>>();
		eventMap = new ConcurrentHashMap<String, EventList>();
	}

	/**
	 * 
	 * @param eventList
	 * @param fistIni
	 *            true 实现延迟载入,false 第一次初始化
	 */
	public void addEventDefine(List<Event> eventList, boolean fistIni) {
		if (UtilValidate.isNotEmpty(eventList)) {
			for (Event e : eventList) {
				addEvent(e, fistIni);
			}
		}
	}

	private Observer getObserver(final Event e) {
		String facClassName = e.getFac();
		try {
			log.debug("facClassName is {}", facClassName);
			ObjectFact fac = (ObjectFact) ObjectType.getInstance(facClassName);// 注意这样,一般需要static来实现共享
			String observerClass = e.getClassKey();
			log.debug("Observer class is {}", observerClass);
			Observer result = (Observer) fac.createObject(observerClass);
			result.setCondition(e.getCondition());
			result.setUserDefSync(e.isSync());

			result.setId(e.getId());
			result.setName(e.getName());
			return result;
		} catch (Exception e1) {
			throw new RuntimeException(e1);
		}

	}

	private boolean addEvent(final Event o,final  boolean fistIni) {

		if (o == null) {
			throw new NullPointerException();
		}
		EventList obs;
		boolean result;

		if (eventMap.containsKey(o.getEventid())) {
			obs = eventMap.get(o.getEventid());
			if (obs.contains(o)) {
				result = false;
				log.warn("may duplicate id {} ,event id:{} ", o.getId(), o.getEventid());
			} else {
				obs.add(o);
				result = true;
			}

		} else {
			obs = new EventList();
			eventMap.putIfAbsent(o.getEventid(), obs);
			obs = eventMap.get(o.getEventid());
			obs.add(o);
			result = true;
		}
		if (!fistIni && o.isSingleton()) {
			registeObserver(o.getEventid(), this.getObserver(o));
		}

		return result;

	}

	private List<Observer> getEventList(final String eventId) {
		EventList el = eventMap.get(eventId);
		if (el == null || el.isEmpty()) {
			return Collections.emptyList();
		}
		List<Observer> result = null;
		List<Observer> tem = getSingeTonObserver(eventId);// 大部分只有SingeTon
		List<Event> pl = el.getPrototypeList();
		if (UtilValidate.isNotEmpty(pl)) {
			for (Event e : pl) {
				if (e.isEnsable()) {

					if (result == null) {
						result = new ArrayList<Observer>();
					}
					result.add(getObserver(e));

				} else {
					log.info("event {} is dis able", e.getName());
				}
			}
		}
		if (result == null) {
			return tem;
		} else {
			result.addAll(tem);
			return result;
		}
	}

	/**
	 * 注册监听者,注意只能是单例的
	 * 
	 * @param eventId
	 * @param obs
	 * @return
	 */
	public boolean registeObserver(final String eventId,final  Observer obs) {
		log.debug("register Observer for event {},must be Singleton,Observer :{}", eventId, obs);
		if (obs == null) {
			log.info("Observer is null");
			return false;
		}
		List<Observer> obeserver = getSingeTonObserver(eventId);
		if (obeserver.contains(obs)) {
			log.info("alreader contains:{}", obs);
			return false;
		} else {
			return obeserver.add(obs);
		}
	}

	public boolean unRegisteObserver(String eventId, Observer obs) {
		log.debug("unRegisteObserver Observer for event {} ,Observer :{}", eventId, obs);
		List<Observer> obeserver = getSingeTonObserver(eventId);

		return obeserver.remove(obs);

	}

	private List<Observer> getSingeTonObserver(final String eventId) {
		List<Observer> result = observerMap.get(eventId);
		if (result == null) {
			log.info("create Singleton Observer for {} ", eventId);
			// result = new CopyOnWriteArrayList<Observer>();
			result = observerMap.putIfAbsent(eventId, new CopyOnWriteArrayList<Observer>());
			if (result == null) {

				EventList el = eventMap.get(eventId);
				if (el != null) {
					result = observerMap.get(eventId);
					List<Event> eventList = el.getSingeTonList();
					if (UtilValidate.isNotEmpty(eventList)) {
						for (Event e : eventList) {
							if (e.isEnsable()) {
								result.add(getObserver(e));
							} else {
								log.info("event {} is dis able", e.getName());
							}
						}
					}
				}
			} else {
				log.info("already add size:{}", result.size());
			}
		}
		return result;
	}

	// public void deleteObserver(String eventId, Observer o) {
	// getEventList(eventId).remove(o);
	// }

	/**
	 * 按照事件 id删除,注意,如果Observer已经初始化,这不能删除Observer
	 * @param event
	 */
	public boolean deleteObserver(final Event event) {
		boolean result = false;
		EventList el = eventMap.get(event.getEventid());
		if (el == null || el.isEmpty()) {
			return result;
		}
		List<Event> eventList = el.getSingeTonList();
		log.debug("remove SingeTon Observer lisnerId id is {}", event.getId());
		if (UtilValidate.isNotEmpty(eventList)) {
			for (int i = eventList.size() - 1; i > -1; i--) {
				Event e = eventList.get(i);
				if (e.getId().equals(event.getId())) {
					eventList.remove(i);
					result = true;
				}
			}
		}
		eventList = el.getPrototypeList();
		log.debug("remove Prototype Observer lisnerId id is {}", event.getId());
		if (UtilValidate.isNotEmpty(eventList)) {
			for (int i = eventList.size() - 1; i > -1; i--) {
				Event e = eventList.get(i);
				if (e.getId().equals(event.getId())) {
					eventList.remove(i);
					result = true;
				}
			}
		}
		// 删除监听器,如果存在
		Observer o = this.getObserver(event);
		this.unRegisteObserver(event.getEventid(), o);
		return result;

	}

	public void disableEvent(final String eventId,final  String lisnerId) {
		EventList el = eventMap.get(eventId);
		if (el == null || el.isEmpty()) {
			return;
		}
		log.debug("disable Observer lisnerId id is {}", lisnerId);

		List<Event> eventList = el.getPrototypeList();
		if (UtilValidate.isNotEmpty(eventList)) {
			for (int i = eventList.size() - 1; i > -1; i--) {
				Event e = eventList.get(i);
				if (e.getId().equals(lisnerId)) {
					e.setisEnable("false");
					break;
				}
			}
		}
		eventList = el.getSingeTonList();
		if (UtilValidate.isNotEmpty(eventList)) {
			for (int i = eventList.size() - 1; i > -1; i--) {
				Event e = eventList.get(i);
				if (e.getId().equals(lisnerId)) {
					e.setisEnable("false");
					break;
				}

			}
		}
	}

	@Override
	public final void publishEvent(BaseEvent o, Map<String, ?> context, Object... arg) throws NotRollbackExcetion {
		String eventName = o.getEventName();
		log.debug("publishEvent {}", eventName);
		List<Observer> obs = getEventList(eventName);
		if (UtilValidate.isNotEmpty(obs)) {
			for (Observer obsr : obs) {
				if (obsr.isRun(o, context)) {
					try {
						obsr.update(o, arg);
					} catch (NotRollbackExcetion e) {
						log.warn("publishEvent {} is NotRollbackExcetion error, message:{}", obsr.getClass().getName(), e);
						log.warn("update error", e);
					} catch (Throwable e) {
						log.error("publishEvent error :" + obsr.getClass());
						log.error("publishEvent error", e);
						throw new RuntimeException(e);
					}

				} else {
					log.info("{} is not run,isRun return false", obsr.getName());
				}

			}
		} else {
			log.debug("no listener for {}", eventName);
		}

	}

	@Override
	public final void publishEvent(BaseEvent o, Object... arg) throws NotRollbackExcetion {

		publishEvent(o, o.getContext(), arg);

	}

	/**
	 * 删除事件定义,已经生效的监听器不会删除
	 * 
	 * @param eventId
	 * @return
	 */
	public EventList deleteObserversDef(final String eventId) {
		return eventMap.remove(eventId);
	}

	public int countObservers(final String eventId) {
		return getEventList(eventId).size();
	}

	public List<Observer> getObservers(final String eventId) {
		return Collections.unmodifiableList(getEventList(eventId));
	}

	@Override
	public boolean hasObserver(final String eventId) throws NotRollbackExcetion {
		return eventMap.containsKey(eventId);
	}

}
